package com.itheima.balance;

import com.itheima.weight.WeightMeta;
import com.itheima.weight.WeightRandomUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.reactive.DefaultResponse;
import org.springframework.cloud.client.loadbalancer.reactive.EmptyResponse;
import org.springframework.cloud.client.loadbalancer.reactive.Request;
import org.springframework.cloud.client.loadbalancer.reactive.Response;
import org.springframework.cloud.loadbalancer.core.NoopServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.http.HttpHeaders;
import org.springframework.util.ObjectUtils;
import reactor.core.publisher.Mono;

import java.util.*;

/*****
 * @Author:
 * @Description:实现不同策略筛选实例
 ****/
public class GrayLoadBalancer implements ReactorServiceInstanceLoadBalancer {

    //服务列表
    private ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;
    //服务ID号
    private  String serviceId;

    public GrayLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider, String serviceId) {
        this.serviceId = serviceId;
        this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;
    }

    /*****
     * 根据不同策略选择指定服务实例
     * @param request：用户请求信息封装对象
     * @return
     */
    @Override
    public Mono<Response<ServiceInstance>> choose(Request request) {
        //获取所有请求头
        HttpHeaders headers = (HttpHeaders) request.getContext();

        //服务列表不为空
        if (this.serviceInstanceListSupplierProvider != null) {
            //获取有效的实例对象
            ServiceInstanceListSupplier supplier = this.serviceInstanceListSupplierProvider.getIfAvailable(NoopServiceInstanceListSupplier::new);
            //获取有效实例集合
            Mono<List<ServiceInstance>> next = supplier.get().next();
            //按照指定路由规则查找符合的实例对象
            return supplier.get().next().map(list->getInstanceResponse(list,headers));
        }
        return null;
    }


    /******
     * 获取实例
     * @param instances
     * @return
     */
    private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances,HttpHeaders headers) {
        //找不到实例
        if (instances.isEmpty()) {
            return new EmptyResponse();
        }else{
            //IP城市切流
            String city = headers.getFirst("city");
            if(!StringUtils.isEmpty(city)){
                //IP切流
                return getServiceInstanceResponseByMtName(instances,city,"city");
            }
            //获取版本号
            String versionNo = headers.getFirst("version");
            //权重路由、  根据版本号+权重路由
            return StringUtils.isEmpty(versionNo)? getServiceInstanceResponseWithWeight(instances) :
                    getServiceInstanceResponseByMtName(instances,versionNo,"version");
        }
    }

    /**
     * 指定属性切流（根据版本进行分发|IP切流）
     * @param instances
     * @param versionNo
     * @return
     */
    private Response<ServiceInstance> getServiceInstanceResponseByMtName(List<ServiceInstance> instances,String versionNo,String mtname) {
        Map<String,String> versionMap = new HashMap<String,String>();
        //versionMap.put("version",versionNo);
        versionMap.put(mtname,versionNo);
        //去除重复
        final Set<Map.Entry<String,String>> attributes = Collections.unmodifiableSet(versionMap.entrySet());

        //定义集合，存储所有复合版本的实例
        List<ServiceInstance> serviceInstances = new ArrayList<ServiceInstance>();
        for (ServiceInstance instance : instances) {
            //当前实例的元数据信息
            Map<String,String> metadata = instance.getMetadata();
            //元数据信息中是否包含当前版本号信息
            if(metadata.entrySet().containsAll(attributes)){
                serviceInstances.add(instance);
            }
        }
        //权重分发
        return getServiceInstanceResponseWithWeight(serviceInstances);
    }

    /**
     * 根据在nacos中配置的权重值，进行分发
     * @param instances
     * @return
     */
    private Response<ServiceInstance> getServiceInstanceResponseWithWeight(List<ServiceInstance> instances) {
        //记录所有实例权重
        Map<ServiceInstance,Integer> weightMap = new HashMap<ServiceInstance,Integer>();
        //循环所有实例
        for (ServiceInstance instance : instances) {
            //获取当前实例元信息
            Map<String,String> metadata = instance.getMetadata();
            //如果当前元信息包含权重key
            if(metadata.containsKey("nacos.weight")){
                //key=实例对象   value=权重
                weightMap.put(instance,Double.valueOf(metadata.get("nacos.weight")).intValue());
            }
        }

        //将weightMap转换成元素权重对象
        WeightMeta<ServiceInstance> weightMeta = WeightRandomUtils.buildWeightMeta(weightMap);

        //返回重随机对象
        ServiceInstance serviceInstance = null;
        if(!ObjectUtils.isEmpty(weightMeta) && !ObjectUtils.isEmpty(serviceInstance=weightMeta.random())){
            return new DefaultResponse(serviceInstance);
        }
        //返回空实例
        return new EmptyResponse();
    }
}
